package com.skyyan.netty.jjf.codec;


import cn.hutool.core.codec.BCD;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import com.skyyan.netty.jjf.protocol.*;
import com.skyyan.netty.jjf.util.CardNumberUtil;
import com.skyyan.netty.jjf.util.JJFByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;

/**
 * 生产级Netty解码器，使用ReplayingDecoder安全地处理粘包/半包问题
 *
 * 解码状态机：
 * 1. WAIT_FRAME_START1: 等待第一个起始符 68H
 * 2. WAIT_LENGTH: 读取长度字段 (1字节)
 * 3. WAIT_FRAME_START2: 等待第二个起始符 68H
 * 4. WAIT_DATA_AND_CRC: 读取数据和CRC
 * 5. WAIT_FRAME_END: 等待结束符 16H
 * @author skyyan
 */
public class JjfMessageDecoder extends ReplayingDecoder<JjfMessageDecoder.DecoderState> {

    private static final Logger log = LoggerFactory.getLogger(JjfMessageDecoder.class);

    /**
     * 解码器状态枚举
     */
    public enum DecoderState {
        WAIT_FRAME_START1,
        WAIT_LENGTH,
        WAIT_FRAME_START2,
        WAIT_DATA_AND_CRC,
        WAIT_FRAME_END
    }

    private int expectedLength;

    public JjfMessageDecoder() {
        super(DecoderState.WAIT_FRAME_START1);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

        try {
            switch (state()) {
                case WAIT_FRAME_START1:
                    decodeFrameStart1(in);
                    break;
                case WAIT_LENGTH:
                    decodeLength(in);
                    break;
                case WAIT_FRAME_START2:
                    decodeFrameStart2(in);
                    break;
                case WAIT_DATA_AND_CRC:
                    decodeDataAndCrc(ctx, in, out);
                    break;
                case WAIT_FRAME_END:
                    decodeFrameEnd(in);
                    break;
                default:
                    throw new IllegalStateException("未知的解码器状态: " + state());
            }
        } catch (Exception e) {
            log.error("解码过程中发生异常，状态: {}", state(), e);
            // 重置状态机，从头开始解码
            checkpoint(DecoderState.WAIT_FRAME_START1);
            throw e;
        }
    }

    /**
     * 解码第一个帧起始符
     */
    private void decodeFrameStart1(ByteBuf in) {
        byte start1 = in.readByte();
        if (start1 != FrameConstants.FRAME_START) {
            log.debug("跳过无效字节: 0x{}", String.format("%02X", start1));
            // 继续在当前状态等待有效的起始符
            return;
        }

        log.debug("找到第一个起始符: 0x{}", String.format("%02X", start1));
        checkpoint(DecoderState.WAIT_LENGTH);
    }


    /**
     * 解码长度字段（修改为：1字节长度）
     */
    private void decodeLength(ByteBuf in) {
        // 读取1个字节作为长度（无符号）
        int length = in.readUnsignedByte(); // 使用 readUnsignedByte() 防止符号扩展
        log.info("解码长度(DEC):{}, (HEX):0x{}", length, String.format("%02X", length));
        if (length < 0 || length > 255) { // 实际上 readUnsignedByte 返回 0~255，此判断可简化
            log.warn("无效的长度字段（超出1字节范围）: {}, 重新开始解码", length);
            checkpoint(DecoderState.WAIT_FRAME_START1);
            return;
        }

        // 可加业务层限制，如最大只接受 200 字节
        if (length > 200) {
            log.warn("长度超出应用限制: {}, 最大允许200", length);
            checkpoint(DecoderState.WAIT_FRAME_START1);
            return;
        }

        this.expectedLength = length;
        log.debug("读取到长度字段（1字节）: {}", expectedLength);
        checkpoint(DecoderState.WAIT_FRAME_START2);
    }
    /**
     * 解码第二个帧起始符
     */
    private void decodeFrameStart2(ByteBuf in) {
        byte start2 = in.readByte();
        if (start2 != FrameConstants.FRAME_START) {
            log.warn("第二个起始符无效: 0x{}, 重新开始解码", String.format("%02X", start2));
            checkpoint(DecoderState.WAIT_FRAME_START1);
            return;
        }

        log.debug("找到第二个起始符: 0x{}", String.format("%02X", start2));
        checkpoint(DecoderState.WAIT_DATA_AND_CRC);
    }

    /**
     * 解码数据和CRC
     */
    private void decodeDataAndCrc(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        System.out.println("----decodeDataAndCrc----expectedLength="+expectedLength);
        // 读取数据部分
        byte[] data = new byte[expectedLength];
        in.readBytes(data);

        // 读取CRC
        byte receivedCrc = in.readByte();

        // 构造完整数据包用于打印输出
        byte[] fullPacket = new byte[3 + expectedLength + 2]; // 0x68 + length + 0x68 + data + crc + 0x16
        fullPacket[0] = FrameConstants.FRAME_START;
        fullPacket[1] = (byte) expectedLength;
        fullPacket[2] = FrameConstants.FRAME_START;
        System.arraycopy(data, 0, fullPacket, 3, expectedLength);
        fullPacket[3 + expectedLength] = receivedCrc;
        fullPacket[3 + expectedLength + 1] = FrameConstants.FRAME_END;



        // 打印完整数据包的十六进制内容
        log.info("服务器下发完整数据包(HEX): {}", bytesToHexWithSpaces(fullPacket));
        log.info("数据域(HEX): {}", bytesToHexWithSpaces(data));
        log.info("CRC(HEX): 0x{}", String.format("%02X", receivedCrc));

        // 验证CRC
        log.info("crc验证---");

        //=====crc 验证

        // ✅ 1. 构造用于 CRC 计算的数据：[68][L][68] + data
        int crcDataLength = 3 + expectedLength; // 3 (68 L 68) + expectedLength (data域)
        byte[] dataForCrc = new byte[crcDataLength];

        // 填充帧头
        dataForCrc[0] = FrameConstants.FRAME_START; // 0x68
        dataForCrc[1] = (byte) expectedLength;      // L
        dataForCrc[2] = FrameConstants.FRAME_START; // 0x68
        // 填充数据域（C, A, AFN, UserData）
        System.arraycopy(data, 0, dataForCrc, 3, expectedLength);

        /**
         * TODO crc验证
         */

        byte calculatedCrc = Crc8Util.calculate(dataForCrc);
        if (receivedCrc != calculatedCrc) {
            log.error("CRC校验失败! 接收: 0x{}, 计算: 0x{}, 数据长度: {}",
                     String.format("%02X", receivedCrc),
                     String.format("%02X", calculatedCrc),
                     expectedLength);
            checkpoint(DecoderState.WAIT_FRAME_END); // 仍然需要读取结束符
            return;
        }
        log.debug("CRC校验通过: 0x{}", String.format("%02X", calculatedCrc));



        // 解析数据为Message对象
        Message message = parseDataToMessage(data);
        if (message != null) {
            out.add(message);
            log.info("解码成功: {}", message);
        } else {
            log.warn("无法解析数据为有效的Message对象");
        }
        // 避免空指针异常，只在message非空时调用toString方法
        if (message != null) {
            message.toString();
        }
        checkpoint(DecoderState.WAIT_FRAME_END);
    }

    /**
     * 解码帧结束符
     */
    private void decodeFrameEnd(ByteBuf in) {
        byte end = in.readByte();
        if (end != FrameConstants.FRAME_END) {
            log.warn("结束符无效: 0x{}", String.format("%02X", end));
        } else {
            log.debug("找到结束符: 0x{}，帧解码完成", String.format("%02X", end));
        }

        // 重置状态机，准备解码下一帧
        checkpoint(DecoderState.WAIT_FRAME_START1);
    }
    public static String bytesToHex(byte[] data) {
        StringBuilder sb = new StringBuilder();
        for (byte b : data) {
            sb.append(String.format("%02x", b & 0xFF));
        }
        return sb.toString();
    }
    /**
     * 将字节数组转换为带空格分隔的十六进制字符串
     * @param data 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHexWithSpaces(byte[] data) {
        if (data == null || data.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < data.length; i++) {
            if (i > 0) {
                sb.append(" ");
            }
            sb.append(String.format("%02X", data[i] & 0xFF));
        }
        return sb.toString();
    }


    /**
     * 从数组中解析出AFN，上行报文和下行报文格式不一样
     * 上行报文：控制域(1字节) + 地址域(变长) + AFN(1字节) + 数据域(变长)
     * 下行报文：控制域(1字节) + AFN(1字节) + 数据域(变长)
     *
     * @param data 报文数据
     * @return AFN 字节值
     * @throws IllegalArgumentException 如果数据为空或长度不足
     */
    private byte getAfn(byte[] data) {
        if (data == null || data.length < 2) {
            throw new IllegalArgumentException("报文数据为空或长度不足");
        }

        byte controlByte = data[0];

        if (controlByte == FrameConstants.CONTROL_DOWNLINK) {
            // 下行报文：控制域 + AFN + 数据域
            // AFN 位于索引 1
            return data[1];
        } else if (controlByte == FrameConstants.CONTROL_UPLINK) {
            // 上行报文：控制域 + 地址域 + AFN + 数据域
            // 根据协议，部分上行报文地址域为19字节，标准地址域为9字节
            // AFN位于控制域之后，地址域之后
            int addressLength = getAddressFieldLength(data);
            int afnIndex = 1 + addressLength;

            if (data.length < afnIndex + 1) {
                throw new IllegalArgumentException("上行报文长度不足，无法读取AFN");
            }
            return data[afnIndex];
        } else {
            throw new IllegalArgumentException("未知的控制域类型: " + String.format("0x%02X", controlByte));
        }
    }

    /**
     * 根据上行报文数据确定地址域长度
     *
     * @param data 上行报文数据
     * @return 地址域长度
     */
    private int getAddressFieldLength(byte[] data) {
        // 根据协议规范，C.6、C.7、C.9等报文的上行地址域为19字节
        // 其他报文使用标准9字节地址域
        if (data.length >= 2) {
            byte afn = data[1 + 19]; // 假设是19字节地址域时AFN的位置

            // 检查AFN是否为需要19字节地址域的类型
            switch (afn) {
                case FrameConstants.AFN_BALANCE_QUERY:   // C.6 余额查询
                case FrameConstants.AFN_RECHARGE:        // C.7 充值
                case FrameConstants.AFN_WATER_ELECTRICITY_PRICE_QUERY:       // C.8 阶梯水电价查询
                case FrameConstants.AFN_OPEN_CARD:       // C.9 开卡
                    return 19;
                default://c.4 c.5
                    return 9; // 标准地址域长度
            }
        }

        // 默认返回标准地址域长度
        return 9;
    }

    /**
     * 将数据字节数组解析为Message对象
     */
    private Message parseDataToMessage(byte[] data) {
        /**
         * data 数据包含 控制域 1字节----|AFN1字节----|数据域
         * 也就是数据域是从第三个字节开始的 位数是index=2
         */
        log.info("数据报文解析:{}",bytesToHex(data));
//        if(data.length<7){
//            log.error("数据长度不足，无法解析为Message: {}", data.length);
//            return null;
//        }
        if(data.length<3){
            log.error("数据长度不足，无法解析为Message: {}", data.length);
            return null;
        }
        log.info("parseDataToMessage--data长度:{}",data.length);
        /**
         * 此data数据格式为 服务器报文下发 格式 |---控制域C--|---AFN---|地址域or 数据域
         */
        Message message=null;

        byte afn=getAfn(data);
        int offset = 0;
        // 解析控制域
        byte control = data[offset++];
        log.info("解析 afn:{} , 控制域:{}",String.format("%02X", afn & 0xFF),String.format("%02X", control & 0xFF));
        //地址域
        byte[] address=null;
        //用户域
        byte[] userData=null;
        try {
            byte stats=data[2];//数据域状态
            String statsHex = String.format("%02X", stats & 0xFF);
            log.info("解析 状态:{}",statsHex);
            //注册结果
            switch (afn){
                case FrameConstants.AFN_REGISTER://c.4注册报文
                    log.info("************注册报文************");
                    userData= Arrays.copyOfRange(data,2,2+16);
//                    RegisterMessage registerMessage=new RegisterMessage(control,address,userData);
                    if(control==FrameConstants.CONTROL_DOWNLINK){
                        //下行报文
                          message=new RegisterMessage.Downlink(address,userData);
                    }else{
                         message=new RegisterMessage.Uplink(address,userData);
                    }
                    message.toString();

                    break;
                case FrameConstants.AFN_REGISTER_CONFIRM://c.5注册确认报文
                    log.info("************注册确认报文************");
                    userData=Arrays.copyOfRange(data,2,2+1);
                    String regResult = HexUtil.encodeHexStr(userData, true);
                    if(regResult.equalsIgnoreCase("00")){
                        log.info("注册结果:{},:{}",regResult,"成功");
                    }else{
                        log.info("注册结果:{},:{}",regResult,"失败");
                    }
                    if(control==FrameConstants.CONTROL_DOWNLINK){
                        //下行报文
                        message=new RegisterConfirmMessage.Downlink(address,userData);
                    }else{
                        message=new RegisterConfirmMessage.Uplink(address,userData);
                    }
                    message.toString();
                    break;

                case FrameConstants.AFN_BALANCE_QUERY://c.6余额查询报文
                    log.info("************余额查询报文************");
                    userData=Arrays.copyOfRange(data,2,2+22);
                    byte[] cardBytes=Arrays.copyOfRange(userData,18-1,18-1+5);
                    String card = HexUtil.encodeHexStr(cardBytes, true);
                    card = CardNumberUtil.bytesToDecimalString(cardBytes);
                    System.out.println("card======"+card);
                    if(control==FrameConstants.CONTROL_DOWNLINK){
                        //下行报文
                        message=new BalanceQueryMessage.Downlink(null,userData);
                    }else{
                        message=new BalanceQueryMessage.Uplink(null,userData);
                    }
                    message.toString();
                    break;
                case FrameConstants.AFN_RECHARGE://c.7充值报文
                    log.info("************充值报文************ data length:{}",data.length);
                    if(control==FrameConstants.CONTROL_DOWNLINK){
                        userData=Arrays.copyOfRange(data,2,2+25);
                        //下行报文
                        message=new RechargeMessage.Downlink(null,userData);
                    }else{
                        //上行报文
                        // 修正地址域和用户数据域的长度计算
                        address=Arrays.copyOfRange(data,1,1+19); // 控制域1 + 地址域19 = 20，address 19
                        afn=data[20];
                        System.out.println("上行 afn="+String.format("%02X", afn & 0xFF));
                        userData=Arrays.copyOfRange(data,21,21+28); // 控制域1 + 地址域19+afn1 = 20，用户数据28字节
                        message=new RechargeMessage.Uplink(address,userData);
                    }

                    message.toString();
                    break;

                case FrameConstants.AFN_WATER_ELECTRICITY_PRICE_QUERY://c.8阶梯水电价查询
                    log.info("************阶梯水电价查询报文************");
                    if(control==FrameConstants.CONTROL_DOWNLINK){
                        userData=Arrays.copyOfRange(data,2,2+19);
                        //下行报文
                        message=new WaterElectricityPriceQueryMessage.Downlink(control,null,userData);
                    }else{
                        //上行报文
                        // 修正地址域和用户数据域的长度计算
                        address=Arrays.copyOfRange(data,1,1+19); // 控制域1 + 地址域19 = 20，address 19
                        afn=data[20];
                       message=new WaterElectricityPriceQueryMessage.Uplink(address);
                    }
                    break;
                case FrameConstants.AFN_OPEN_CARD://c.9阶梯水电价查询确认报文
                    log.info("************开卡报文************");
                    if(control==FrameConstants.CONTROL_DOWNLINK){
                        userData=Arrays.copyOfRange(data,2,2+25);
                        //下行报文
                        message=new OpenCardMessage.Downlink(null,userData);
                    }else{
                        address=Arrays.copyOfRange(data,1,1+19);
                        userData=Arrays.copyOfRange(data,21,21+51);
                        message=new OpenCardMessage.Uplink(address,userData);
                    }

            }
            if(message!=null){
                return message;
            }
            log.debug("解析报文 - Control: 0x{}, AFN: 0x{}, Address长度: {}, UserData长度: {}",
                    String.format("%02X", control),
                    String.format("%02X", afn),
                    address!=null?address.length:0,
                    userData!=null?userData.length:0);

            // 使用工厂创建Message对象
            return MessageFactory.createMessage(control, address, afn, userData);

        } catch (Exception e) {
            log.error("解析数据为Message对象时发生异常", e);
            return null;
        }
    }


    /**
     * 判断指定AFN是否需要扩展地址域(20字节)
     * @param afn AFN功能码
     * @return true表示需要20字节地址域，false表示使用标准9字节地址域
     */
    private boolean requiresExtendedAddress(byte afn) {
        switch (afn) {
            case FrameConstants.AFN_BALANCE_QUERY:  // C.6 余额查询
            case FrameConstants.AFN_RECHARGE:       // C.7 充值 / C.8 查询储水价、电价
                return true;
            default:
                return false;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("解码器发生异常", cause);
        // 重置状态机
        checkpoint(DecoderState.WAIT_FRAME_START1);
        super.exceptionCaught(ctx, cause);
    }
}

